/**
 * projectName: mbzwb-parent
 * fileName: AssessEngine.java
 * packageName: com.palmdoctor.mb.server.assess.engine
 * date: 2021-08-09 15:49
 * copyright(c) 2021 http://www.hydee.cn/ Inc. All rights reserved.
 */
package com.zny.sescore.engine;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zny.sescore.exception.MessageEnum;
import com.zny.sescore.model.AssessModelOption;
import com.zny.sescore.model.AssessModelResult;
import com.zny.sescore.model.AssessModelRule;
import com.zny.sescore.model.vo.AssessModelVO;
import com.zny.sescore.rule.RuleEngineManager;
import com.zny.sescore.rule.constant.DefaultVariableEnum;
import com.zny.sescore.rule.constant.ResultTypeEnum;
import com.zny.sescore.rule.constant.ScoreTypeEnum;
import com.zny.sescore.util.AssertUtil;
import com.zny.sescore.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @version: V1.0
 * @author: zhangnayi
 * @className: AssessEngine
 * @packageName: com.palmdoctor.mb.server.assess.engine
 * @description: 评分得分引擎
 * @data: 2021-08-09 15:49
 **/
@Slf4j
@Service
public class AssessRuleEngine {

    public AssessModelResult eval(AssessModelVO modelVO, Map<String, Object> param) {

        // 构建项目编码->项目得分 map
        Map<String, Object> resultItemMap = new HashMap<>();
        // 循环计算每个项目的得分
        modelVO.getItemList().forEach(item -> {

            String itemCode = item.getItemCode();
            Object itemValue = param.get(itemCode);
            AssertUtil.notNull(itemValue, MessageEnum.ASSESS_PARAM_ERROR);

            ScoreTypeEnum scoreType = ScoreTypeEnum.getByCode(item.getScoreType());
            AssertUtil.notNull(scoreType, MessageEnum.ASSESS_CONFIG_ERROR);

            // 项目得分
            BigDecimal itemScore = BigDecimal.ZERO;
            // 直接得分
            if (scoreType == ScoreTypeEnum.RETURN_SCORE) {
                // 多选积分求和, 单选单个得分
                for (AssessModelOption option : item.getOptionList()) {
                    if (itemValue.toString().contains(option.getOptionCode())) {
                        itemScore = itemScore.add(option.getScore());
                    }
                }
                log.info("计算项目得分 itemName={}, 直接得分={}", item.getItemName(), itemScore);
            }
            // 匹配规则 或 表达式
            else {

                RuleEngineManager engineInstance = RuleEngineManager.instance()
                        .addParam(DefaultVariableEnum.ITEM_VALUE.getCode(), itemValue);

                for (AssessModelRule rule : item.getRuleList()) {
                    String script = rule.getScoreExp();
                    boolean match = engineInstance.setRuleScript(script).match();
                    if (match) {
                        itemScore = rule.getScore();
                        break;
                    }
                }
                log.info("计算项目得分 itemName={}, 匹配规则={}", item.getItemName(), itemScore);
            }

            AssertUtil.notNull(itemScore, MessageEnum.ASSESS_CONFIG_ERROR);
            resultItemMap.put(itemCode, itemScore);
        });

        /*
         * 计算评估结果
         */
        ResultTypeEnum resultType = ResultTypeEnum.getByCode(modelVO.getResultType());
        AssertUtil.notNull(resultType, MessageEnum.ASSESS_CONFIG_ERROR);
        // 得分求和
        if (Objects.equals(resultType, ResultTypeEnum.SUM)) {

            BigDecimal totalScore = resultItemMap.keySet().stream()
                    .map(key -> (BigDecimal) resultItemMap.get(key))
                    .reduce(BigDecimal::add).get();
            resultItemMap.put(DefaultVariableEnum.TOTAL_SCORE.getCode(), totalScore);
            log.info("计算项目总得分 totalScore={}", totalScore);
        }
        // 得分表达式
        else if (Objects.equals(resultType, ResultTypeEnum.EXP_TEXT)) {

            AssertUtil.notNull(modelVO.getResultExp(), MessageEnum.ASSESS_CONFIG_ERROR);
            BigDecimal totalScore = RuleEngineManager.instance()
                    .addParam(resultItemMap)
                    .setRuleScript(modelVO.getResultExp())
                    .eval2();
            resultItemMap.put(DefaultVariableEnum.TOTAL_SCORE.getCode(), totalScore);
            log.info("计算项目得分表达式 totalScore={}, resultExp = {}", totalScore, modelVO.getResultExp());
        }

        // 匹配非默认结果
        AssessModelResult matchResult = null;
        for (AssessModelResult modelResult : modelVO.getResultList()) {
            if (modelResult.getDefaultResult() == null || !modelResult.getDefaultResult()) {
                String script = modelResult.getResultExp();
                boolean match = RuleEngineManager.instance().addParam(resultItemMap)
                        .setRuleScript(script)
                        .match();
                if (match) {
                    matchResult = modelResult;
                    break;
                }
            } else {
                matchResult = modelResult;
            }
        }

        AssertUtil.notNull(matchResult, MessageEnum.ASSESS_CONFIG_ERROR);
        // 计算匹配结果自定义值
        if (StringUtils.isNotEmpty(matchResult.getCustomExp())) {
            String script = matchResult.getCustomExp();
            BigDecimal value = RuleEngineManager.instance().addParam(resultItemMap)
                    .setRuleScript(script)
                    .eval2();
            matchResult.setCustomValue(value);
        }

        log.info("返回默认结果 modelResult={}", JsonUtil.objectToString(matchResult));
        return matchResult;
    }
}